/* EIX - IPC system
 * Copyright (C) 2008-2011 Jorge Luis Zapata
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 * If not, see <http://www.gnu.org/licenses/>.
 */
#include "Eix.h"
#include "eix_private.h"

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif

#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif

#ifdef _WIN32
# include <winsock2.h>
#endif

#define MESSAGE_ID_MAX 0xefffffff
#define MESSAGE_ID_MASK 0x80000000

/*============================================================================*
 *                                  Local                                     *
 *============================================================================*/
/*
 * A message is composed of:
 * +----+----------+------+------+----------
 * | id | reply_to | type | size |
 * +----+----------+------+------+----------
 * <-----header----------------->.<-----body------>
 *
 * the id should be composed of:
 * +---+---+-------------+
 * | T | E |      id     |
 * +---+---+-------------+
 * Where:
 * T is the type: 0 reply, 1, message
 * E is the error: 0 no error, 1 error body
 *
 * or the type should identify the error?
 *
 */

/* the error should not be part of the message, it is a message by itself, in case
 * of a reply, the reply_id is set with the message received and the transport when
 * looking for a reply, it should check that it refers to the registered message
 * or an error message
 */
typedef struct _Eix_Message_Header
{
	uint32_t id; /* id of the message */
	uint32_t reply_to; /* id of the message this reply replies to */
	uint32_t type; /* type of message */
	uint32_t size; /* size of the body */
} Eix_Message_Header;

typedef struct _Eix_Transport_Request
{
	/* the header already in host endianess */
	Eix_Message_Header hdr;
	/* the message descriptor */
	Eix_Message *m;
	/* the payload of the message */
	void *body;
	/* the error pointer in case we need to write there */
	Eix_Error *err;

	/* in case the user prefers a function call */
	Eix_Transport_Reply_Cb cb;
	const void *user_data;
} Eix_Transport_Request;

/* TODO we might want to limit the length of pending_replies or pending_messages
 * to only full a limited number of elements
 */
struct _Eix_Transport
{
	Eix_Transport_State state;
	Eina_Bool serves;
	Eina_Bool needs_read;
	Eina_Bool needs_write;

	/* functions to be called from the lower layer */
	Eix_Transport_Descriptor *descriptor;
	Eix_Transport_Process process;
	void *data;

	/* functions to be called from the upper layer */
	Eix_Transport_Process_Descriptor *process_descriptor;
	void *process_data;

	/* functions to be called from the main loop handler */
	Eix_Transport_Process_Request process_request;
	void *process_request_data;

	/* the output queue is where we store the pending data for send to */
	Eina_Binbuf *outqueue;
	/* the input queue is where we store the pending data to recv from */
	Eina_Binbuf *inqueue;
	/* here we store the messages that are pending a reply */
	Eina_List *pending_replies;
};

/* the mh must be in host endianess */
static inline Eina_Bool _message_is_reply(Eix_Message_Header *mh)
{
	if (MESSAGE_ID_MASK & mh->id) return EINA_FALSE;
	else return EINA_TRUE;
}

/* rid is in host endianess */
static void _message_header_init(Eix_Message_Header *mh,
		unsigned int len, unsigned int type, uint32_t rid)
{
	static unsigned int id = 0;

	/* for mesages we have the topmost bit set to 1, and for
	 * replies it is zero
	 */
	/* make the id know if it is a reply or not */
	mh->id = htonl((~rid & MESSAGE_ID_MASK) | id);
	mh->reply_to = htonl(rid);
	mh->type = htonl(type);
	mh->size = htonl(len);
	/* avoid the overlfow */
	if (id + 1 >= MESSAGE_ID_MAX)
		id = 0;
	else
		id++;
}

static void _reply_process(Eix_Transport *thiz, Eix_Message_Header *mh,
		Eix_Message *m, void *body)
{
	Eix_Transport_Request *rq;
	Eina_List *l, *l_next;
	Eina_Bool found = EINA_FALSE;

	EINA_LIST_FOREACH_SAFE(thiz->pending_replies, l, l_next, rq)
	{
		if (htonl(rq->hdr.id) != mh->reply_to)
			continue;
		DBG("Received a pending reply for %08x, "
				"dequeueing it", mh->reply_to);
		found = EINA_TRUE;
		thiz->pending_replies = eina_list_remove_list(thiz->pending_replies, l);
		break;
	}

	if (!found)
	{
		ERR("Received a reply without expecting it");
		goto not_found;
	}

	/* ok, we found it, unmarshal the body but on the passed in pointer */
	if (body)
	{
		/* check if it is an error and we have a place for it*/
		if (m == EIX_MESSAGE_ERROR)
		{
 			if (rq->err)
				ender_descriptor_native_unmarshal_inplace(m->edd, rq->err, body, mh->size);
			else if (rq->cb)
			{
				Eix_Error err;
				ender_descriptor_native_unmarshal_inplace(m->edd, &err, body, mh->size);
				rq->cb(NULL, body, (void *)rq->user_data);
			}
		}
		else
		{
			if (rq->body)
				ender_descriptor_native_unmarshal_inplace(m->edd, rq->body, body, mh->size);
			else if (rq->cb)
			{
				void *r_body;
				r_body = ender_descriptor_native_unmarshal(m->edd, body, mh->size);
				rq->cb(r_body, NULL, (void *)rq->user_data);
				ender_descriptor_native_free(m->edd, r_body);
			}
		}
	}
	free(rq);
not_found:
	if (body) free(body);
}

static Eina_Bool _message_process(Eix_Transport *thiz, Eix_Message_Header *mh,
		Eix_Message *m, void *body)
{
	Eix_Message *reply;
	Eix_Error error = { 0 };
	Eina_Bool ret = EINA_TRUE;
	void *r_body = NULL;
	void *m_body = NULL;

	if (body)
	{
		m_body = ender_descriptor_native_unmarshal(m->edd, body, mh->size);
	}

	reply = eix_message_reply_get(m);
	if (reply && reply->edd)
	{
		r_body = ender_descriptor_native_new(reply->edd);
	}
	/* call the server/client layer for processing */
	if (thiz->process_descriptor && thiz->process_descriptor->data)
		thiz->process_descriptor->data(m, m_body, r_body, &error, thiz->process_data);
	if (m_body)
	{
		ender_descriptor_native_free(m->edd, m_body);
	}
	/* send the reply */
	if (reply)
	{
		/* in case the error is set, send the error, otherwise send the r_body */
		if (eix_error_is_set(&error))
			ret = eix_transport_send(thiz, EIX_MESSAGE_ERROR, mh->id, &error, NULL, NULL, NULL, NULL, NULL);
		else
			ret = eix_transport_send(thiz, reply, mh->id, r_body, NULL, NULL, NULL, NULL, NULL);

		if (r_body)
		{
			ender_descriptor_native_free(reply->edd, r_body);
		}
	}
	return ret;
}

static void _inqueue_push_data(Eix_Transport *thiz, unsigned char *buf, unsigned int buf_len)
{
	eina_binbuf_append_length(thiz->inqueue, buf, buf_len);
}

/* the message header will be output as host endianess */
static Eina_Bool _inqueue_pop_message(Eix_Transport *thiz,
		Eix_Message_Header *mh, Eix_Message **pm,
		void **pbody)
{
	Eina_Bool ret = EINA_TRUE;
	Eix_Message_Header *mh_p;
	Eix_Message *m;
	void *body = NULL;
	uint32_t body_length;
	const unsigned char *so_far;
	unsigned int len;

	len = eina_binbuf_length_get(thiz->inqueue);

	/* not enough data for a header */
	if (len < sizeof(Eix_Message_Header))
		return EINA_FALSE;
	so_far = eina_binbuf_string_get(thiz->inqueue);
	mh_p = (Eix_Message_Header *)so_far;
	body_length = ntohl(mh_p->size);
	/* not enough data for a complete message */
	if (len < body_length)
		return EINA_FALSE;

	/* ok we have enough data, pick the message and remove
	 * the data from the binbuf
	 */
	/* make the header in host format */
	mh->id = ntohl(mh_p->id);
	mh->reply_to = ntohl(mh_p->reply_to);
	mh->type = ntohl(mh_p->type);
	mh->size = body_length;

	DBG("Received message %p id: %08x rid: %08x type: %d", thiz, mh->id, mh->reply_to, mh->type);
	m = eix_message_find(mh->type);
	if (!m)
	{
		ERR("Message with type %d does not exist, skipping it", mh->type);
		goto done;
	}

	/* we might send messages without body, i.e signals */
	/* We do a memcpy here because eina_binbuf destroys the data when removing
	 * a range, another way to do it is to pass a callback to call
	 */
	if (body_length)
	{
		body = malloc(body_length);
		memcpy(body, so_far + sizeof(Eix_Message_Header), body_length);
	}
	/* set the output variables */
	*pm = m;
	*pbody = body;
	ret = EINA_TRUE;
done:
	/* advance the binbuf or we'll get problems if the message needs a reply
	 * that implies another message
	 */
	eina_binbuf_remove(thiz->inqueue, 0, sizeof(Eix_Message_Header) + body_length);

	return ret;
}

static void _inqueue_message_process(Eix_Transport *thiz, Eix_Message_Header *mh,
		Eix_Message *m, void *m_body)
{
	/* check it the message is a reply and if so, remove the reply
	 * from the list of pending replies and add it to the list
	 * of pending messages
	 */
	if (_message_is_reply(mh))
	{
		_reply_process(thiz, mh, m, m_body);
	}
	/* if it is not a reply but a message, then append it, WHY? */
	else
	{
		_message_process(thiz, mh, m, m_body);
	}
}

static void _inqueue_flush(Eix_Transport *thiz)
{
	Eix_Message_Header mh;
	Eix_Message *m;
	Eina_Bool has_message;
	void *m_body = NULL;

	has_message = _inqueue_pop_message(thiz, &mh, &m, &m_body);
	while (has_message)
	{
		DBG("Dispatching queued input data");
		_inqueue_message_process(thiz, &mh, m, m_body);
		has_message = _inqueue_pop_message(thiz, &mh, &m, &m_body);
	}
}

static Eina_Bool _message_send_full(Eix_Transport *thiz,
		void *data, unsigned int len)
{
	Eix_Transport_Status status;
	unsigned char *d = data;
	size_t sent;

again:
	sent = len;
	status = thiz->descriptor->send(thiz->data, d, &sent);
	if (!status)
	{
		ERR("An error occured when sending the message header");
		return EINA_FALSE;
	}
	else if (status == EIX_TRANSPORT_STATUS_ASYNC || sent < len)
	{
		if (!thiz->descriptor->wait_write(thiz->data))
			return EINA_FALSE;
		/* ok we can try again with the rest */
		len -= sent;
		d += sent;
		goto again;
	}

	return EINA_TRUE;
}

static Eina_Bool _sync(Eix_Transport *thiz)
{
	Eina_Bool ret = EINA_TRUE;

	/* just iterate until we find every reply */
	while (thiz->pending_replies)
	{
		Eix_Transport_Status status;
		unsigned char buf[4096];
		size_t blen = sizeof(buf);

		if (!thiz->descriptor->wait_read(thiz->data))
			return EINA_FALSE;

		status = thiz->descriptor->recv(thiz->data, buf, &blen);
		if (!status)
		{
			ret = EINA_FALSE;
			break;
		}
		_inqueue_push_data(thiz, buf, blen);
		_inqueue_flush(thiz);
	}

	return ret;
}

/* if the message requires a sync reply, it should always be sync, so we need to wait
 * until the whole message is sent, that implies a select() on the fd for writing
 * and sleep until the message has been set or something happens (fd closed) and
 * wait again for a receive.
 */
static Eina_Bool _message_send_sync(Eix_Transport *thiz,
		Eix_Message_Header *mh, Eix_Message *m, void *body,
		unsigned int len, void *rdata, Eix_Error *err,
		Eix_Transport_Reply_Cb cb, const void *user_data)
{
	Eix_Transport_Request *rq;
	Eina_Bool ret = EINA_FALSE;

	/* first flush the output queue to keep the messages in order */
	if (!eix_transport_flush(thiz))
		goto done;

	/* then try to send the header */
	if (!_message_send_full(thiz, mh, sizeof(Eix_Message_Header)))
	{
		ERR("Impossible to send the header");
		goto done;
	}
	/* now send the body */
	if (body)
	{
		if (!_message_send_full(thiz, body, len))
		{
			ERR("Impossible to send the body");
			goto done;
		}
	}
	if (!m->reply)
	{
		WRN("Sending sync message without a reply?");
		goto done;
	}
	/* add this request to the list of pending replies */
	rq = calloc(1, sizeof(Eix_Transport_Request));
	rq->hdr = *mh;
	rq->m = m;
	rq->body = rdata;
	rq->err = err;
	rq->cb = cb;
	rq->user_data = user_data;

	thiz->pending_replies = eina_list_append(thiz->pending_replies, rq);
	/* and finally call _sync() */
	ret = _sync(thiz);

done:
	/* free the marshalled body */
	if (body) free(body);

	return ret;
}

/* if the message does not expect a reply */
/* when sending a message we first try the sync version, and check the return value
 * if it returns an async status, then store the message on the output queue
 * and inform that this transport needs a write
 */
static Eina_Bool _message_send_async(Eix_Transport *thiz,
		Eix_Message_Header *mh, Eix_Message *m, void *body,
		unsigned int len, void *rdata, Eix_Error *err,
		Eix_Transport_Reply_Cb cb, const void *user_data)
{
	Eix_Transport_Status status;
	Eina_Bool ret = EINA_FALSE;
	size_t sent;

	/* first try to send the header */
	sent = sizeof(Eix_Message_Header);
	status = thiz->descriptor->send(thiz->data, mh, &sent);
	if (!status)
	{
		ERR("An error occured when sending the message header");
		return EINA_FALSE;
	}
	else if (status == EIX_TRANSPORT_STATUS_ASYNC || sent < sizeof(Eix_Message_Header))
	{
		/* write it on the output queue */
		eina_binbuf_append_length(thiz->outqueue, (unsigned char *)mh +
				sent, sizeof(Eix_Message_Header) - sent);
		/* also write the body itself */
		if (body)
		{
			eina_binbuf_append_length(thiz->outqueue, body, len);
		}
		/* inform that we need to write too */
		eix_transport_process_request(thiz, thiz->needs_read, EINA_TRUE);
		goto done;
	}
	if (!body) goto done;
	/* now the body */
	sent = len;
	status = thiz->descriptor->send(thiz->data, body, &sent);
	if (!status)
	{
		ERR("An error occured when sending the message body");
		return EINA_FALSE;
	}
	else if (status == EIX_TRANSPORT_STATUS_ASYNC || sent < len)
	{
		/* write it on the output queue */
		eina_binbuf_append_length(thiz->outqueue, (unsigned char *)body +
				sent, len - sent);
		/* inform that we need to write too */
		eix_transport_process_request(thiz, thiz->needs_read, EINA_TRUE);
	}
	/* free the marshalled data */
	free(body);
done:
	ret = EINA_TRUE;
	/* in case we need a reply, store the message in the pending replies */
	if (m->reply)
	{
		Eix_Transport_Request *rq;

		rq = calloc(1, sizeof(Eix_Transport_Request));
		rq->m = m->reply;
		rq->hdr = *mh;
		/* here we store the place where a reply should be set */
		rq->body = rdata;
		/* keep the error pointer */
		rq->err = err;
		rq->cb = cb;
		rq->user_data = user_data;

		thiz->pending_replies = eina_list_append(thiz->pending_replies, rq);
	}
	return ret;
}

/*============================================================================*
 *                                 Global                                     *
 *============================================================================*/
Eix_Transport * eix_transport_new(Eix_Transport_Descriptor *descriptor,
		void *data)
{
	Eix_Transport *thiz;

	/* some safety checks */
	if (!descriptor) return NULL;
	if (!descriptor->send)
	{
		ERR("The descriptor does not implement the 'send' method");
		return NULL;
	}
	if (!descriptor->recv)
	{
		ERR("The descriptor does not implement the 'recv' method");
		return NULL;
	}

	thiz = calloc(1, sizeof(Eix_Transport));
	thiz->data = data;
	thiz->descriptor = descriptor;
	thiz->state = EIX_TRANSPORT_STATE_INIT;
	thiz->inqueue = eina_binbuf_new();
	thiz->outqueue = eina_binbuf_new();

	return thiz;
}

void eix_transport_free(Eix_Transport *thiz)
{
	if (thiz->descriptor->free)
		thiz->descriptor->free(thiz->data);
	free(thiz);
}

void * eix_transport_data_get(Eix_Transport *thiz)
{
	return thiz->data;
}


Eina_Bool eix_transport_connect(Eix_Transport *thiz)
{
	Eix_Transport_State state;

	if (thiz->state != EIX_TRANSPORT_STATE_INIT)
		return EINA_FALSE;
	if (!thiz->descriptor->connect) return EINA_FALSE;
	state = thiz->descriptor->connect(thiz->data);
	if (state == EIX_TRANSPORT_STATE_INIT) return EINA_FALSE;

	thiz->state = state;
	if (state == EIX_TRANSPORT_STATE_CONNECTED)
	{
		eix_transport_process_add(thiz, thiz);
	}
	return EINA_TRUE;
}

Eina_Bool eix_transport_serve(Eix_Transport *thiz)
{
	Eix_Transport_State state;

	if (thiz->state != EIX_TRANSPORT_STATE_INIT)
		return EINA_FALSE;

	if (!thiz->descriptor->serve) return EINA_FALSE;
	state = thiz->descriptor->serve(thiz->data);
	if (state != EIX_TRANSPORT_STATE_INIT)
		thiz->serves = EINA_TRUE;
	thiz->state = state;
	return EINA_TRUE;
}

/* when flushing we should send everything that is on our outqueue */
Eina_Bool eix_transport_flush(Eix_Transport *thiz)
{
	unsigned int len;

	len = eina_binbuf_length_get(thiz->outqueue);
	while (len)
	{
		Eix_Transport_Status status;
		size_t sent = len;
		void *data;

		data = eina_binbuf_string_get(thiz->outqueue);
		/* wait until we can send something */
		if (!thiz->descriptor->wait_write(thiz->data))
			return EINA_FALSE;

		/* start sending pending messages */
		/* just send the data we have on the outqueue */
		status = thiz->descriptor->send(thiz->data, data, &sent);
		if (status == EIX_TRANSPORT_STATUS_FAILED)
		{
			ERR("Failed flushing");
			return EINA_FALSE;
		}
		/* async or less data does not care, we need to send everything */
		len -= sent;
	}

	return EINA_TRUE;
}

/* when syncing, first we flush everything that is on the outqueue
 * and then we wait until every pending reply is received
 */
Eina_Bool eix_transport_sync(Eix_Transport *thiz)
{
	if (!eix_transport_flush(thiz)) return EINA_FALSE;
	return _sync(thiz);
}

Eina_Bool eix_transport_recv(Eix_Transport *thiz)
{
	Eix_Transport_Status status;
	Eina_Bool ret = EINA_FALSE;
	unsigned char buf[4096];
	size_t length = sizeof(buf);

	if (thiz->state == EIX_TRANSPORT_STATE_INIT)
		return EINA_FALSE;

	status = thiz->descriptor->recv(thiz->data, buf, &length);
	if (status == EIX_TRANSPORT_STATUS_FAILED)
	{
		ERR("Error when reading from the transport %p", thiz->process_descriptor->remove);
		if (thiz->process_descriptor->remove)
			thiz->process_descriptor->remove(thiz->process_data);
		return EINA_FALSE;
	}
	else if (status == EIX_TRANSPORT_STATUS_ASYNC)
	{
		ERR("Async read?");
		/* TODO check if the transport has the read flag already set
		 * if not, set it again
		 */
	}
	else
	{
		_inqueue_push_data(thiz, buf, length);
		_inqueue_flush(thiz);
		ret = EINA_TRUE;
#if 0
		ret = _message_recv(thiz, buf, length);
#endif
	}
	DBG("Ret = %d", ret);
	return ret;
}

/* the rid should be in host endianes */
Eina_Bool eix_transport_send(Eix_Transport *thiz, Eix_Message *m,
		uint32_t rid, void *data, void *rdata, Eix_Error *error,
		uint32_t *id, Eix_Transport_Reply_Cb cb, const void *user_data)
{
	Eix_Message_Header mh;
	Eina_Bool ret = EINA_FALSE;
	void *body = NULL;
	unsigned int len = 0;

	/* check that the transport state is connected */
	if (thiz->state == EIX_TRANSPORT_STATE_INIT)
		goto done;

	/* you should not write into the socket that accepts connections */
	if (thiz->serves)
	{
		WRN("You should use the client, not the server");
		return EINA_FALSE;
	}

	if (!m) return EINA_FALSE;
	/* something wrong with the data passed in */
	if (m->edd && !data)
	{
		WRN("Sending a message expecting a payload without payload");
		goto done;
	}
	else if (data && !m->edd)
	{
		WRN("Sending a payload when the message does not require it");
		goto done;
	}
	if (m->edd)
	{
		body = ender_descriptor_native_marshal(m->edd, data, &len);
		if (!body)
		{
			WRN("Impossible to marshal the message %d", m->type);
			goto done;
		}
		DBG("Data marshalled with size %d at %p", len, body);
		/* FIXME for now until we fix the eet serializer or eet itself :-/ */
#define MEMDEBUG 0
#if MEMDEBUG
		{
			int i = 0;
			/* for memory debug */
			while (i < len)
				printf("%c", ((unsigned char *)body)[i++]);
			printf("\n");
		}
#endif
	}

	/* initialize the header */
	_message_header_init(&mh, len, m->id, rid);
	DBG("Sending message %p id: %08x rid: %08x type: %d len: %d", thiz,
			ntohl(mh.id), rid, m->id, len);
	if (m->type == EIX_MESSAGE_SIGNAL || m->type == EIX_MESSAGE_REPLY_ASYNC)
		ret = _message_send_async(thiz, &mh, m, body, len, rdata, error, cb, user_data);
	else
		ret = _message_send_sync(thiz, &mh, m, body, len, rdata, error, cb, user_data);
	/* TODO in case the send implementation fails, we need to remove the endpoint */
	if (id) *id = mh.id;
	DBG("Ret = %d", ret);
done:
	return ret;
}

void eix_transport_state_set(Eix_Transport *thiz, Eix_Transport_State state)
{
	thiz->state = state;
}

void eix_transport_process(Eix_Transport *thiz, Eina_Bool can_read,
		Eina_Bool can_write)
{
	Eix_Transport_State state;

	//printf("%d %d -> %d %d\n", thiz->needs_read, thiz->needs_write, can_read, can_write);
	if (thiz->state == EIX_TRANSPORT_STATE_INIT)
		return;
	if (!can_read && !can_write)
		return;

	/* in case we have something in the outqueue and the can_write is set
	 * try to send it
	 */
	if (can_write && eina_binbuf_length_get(thiz->outqueue))
	{
		/* the flush will block, dont call it */
		eix_transport_flush(thiz);
	}

	if (!thiz->process) return;
	state = thiz->process(thiz->data,
		thiz->state, can_read, can_write);

	thiz->state = state;
}

void eix_transport_process_descriptor_set(Eix_Transport *thiz,
		Eix_Transport_Process_Descriptor *descriptor,
		void *user_data)
{
	thiz->process_descriptor = descriptor;
	thiz->process_data = user_data;
}

void eix_transport_process_add(Eix_Transport *thiz, Eix_Transport *c)
{
	/* nobody listenting, then just delete it */
	if (!thiz->process_descriptor || !thiz->process_descriptor->add)
	{
		return;
	}
	/* whenever we add another end point, set it to connected */
	if (c) eix_transport_state_set(c, EIX_TRANSPORT_STATE_CONNECTED);
	thiz->process_descriptor->add(c, thiz->process_data);
}

void eix_transport_process_request(Eix_Transport *thiz, Eina_Bool to_read,
		Eina_Bool to_write)
{
	thiz->needs_read = to_read;
	thiz->needs_write = to_write;
	if (thiz->process_request)
		thiz->process_request(thiz, thiz->needs_read, thiz->needs_write,
				thiz->process_request_data);
}

void eix_transport_process_set(Eix_Transport *thiz,
		Eix_Transport_Process process)
{
	thiz->process = process;
}
/*============================================================================*
 *                                   API                                      *
 *============================================================================*/
/**
 *
 */
EAPI Eix_Transport_State eix_transport_state_get(Eix_Transport *thiz)
{
	return thiz->state;
}

/**
 *
 */
EAPI Eina_Bool eix_transport_serves(Eix_Transport *thiz)
{
	return thiz->serves;
}

/**
 *
 */
EAPI void eix_transport_process_required(Eix_Transport *thiz, Eina_Bool *to_read,
		Eina_Bool *to_write)
{
	if (to_read) *to_read = EINA_FALSE;
	if (to_write) *to_write = EINA_FALSE;

	if (!thiz) return;
	if (to_read) *to_read = thiz->needs_read;
	if (to_write) *to_write = thiz->needs_write;
}

/**
 *
 */
EAPI void eix_transport_process_request_set(Eix_Transport *thiz,
		Eix_Transport_Process_Request req, void *data)
{
	thiz->process_request = req;
	thiz->process_request_data = data;
}
